# -*-coding:utf-8-*-
from abc import ABCMeta, abstractmethod
import collections


class AbstractBettingStrategy(metaclass=ABCMeta):
    __slots__ = ()

    @abstractmethod
    def bet(self, hand):
        return 1

    @abstractmethod
    def record_win(self, hand):
        pass

    @abstractmethod
    def record_loss(self, hand):
        pass

    @classmethod
    def __subclasscheck__(cls, subclass):
        if cls is Hand:
            if (any("bet" in b.__dict__ for B in subclass.__mro__) and any(
                    "record_win" in B.__dict__ for B in subclass.__mro__) and any(
                "record_los" in B.__dict__ for B in subclass.__mro__)):
                return True
        return NotImplemented


class Simple(AbstractBettingStrategy):
    def bet(self, hand):
        return 1

    def record_win(self, hand):
        pass

    def record_loss(self, hand):
        pass


class Simple_Broken(AbstractBettingStrategy):
    def bet(self, hand):
        return 1


class Power4(collections.abc.Callable):
    def __call__(self, x, n):
        if n == 0:
            return 1
        elif n % 2 == 1:
            return self.__call__(x, n - 1) * x
        else:
            t = self.__call__(x, n // 2)
            return t * t


class Power5(collections.abc.Callable):
    def __init__(self):
        self.memo = {}

    def __call__(self, x, n):
        if (x, n) not in self.memo:
            if n == 0:
                self.memo[x, n] = 1
            elif n % 2 == 1:
                self.memo[x, n] = self.__call__(x, n - 1) * x
            elif n % 2 == 0:
                t = self.__call__(x, n // 2)
                self.memo[x, n] = t * t
            else:
                raise Exception("Logic Error")
            return self.memo[x, n]


class BettingStrategy:
    def __init__(self):
        self.win = 0
        self.loss = 0

    def __call__(self):
        return 1


class BettingMartingale(BettingStrategy):
    def __init__(self):
        self._win = 0
        self._loss = 0
        self.stage = 1

    @property
    def win(self): return self._win

    @win.setter
    def win(self, value):
        self._win = value
        self.stage = 1

    @property
    def loss(self): return self._loss

    @loss.setter
    def loss(self, value):
        self._loss = value
        self.stage *= 2

    def __call__(self):
        return self.stage


class BettingMartingale2(BettingStrategy):
    def __init__(self):
        self.win = 0
        self.loss = 0
        self.stage = 1

    def __setattr__(self, name, value):
        if name == 'win':
            self.stage = 1
        elif name == 'loss':
            self.stage *= 2
        super().__setattr__(name, value)

    def __call__(self):
        return self.stage


if __name__ == '__main__':
